﻿using System;
using System.IO;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Reflection;
using System.Threading;
using System.Runtime.InteropServices;
using System.Web;
using System.Web.Hosting;
using System.CodeDom;
using System.CodeDom.Compiler;

using Microsoft.CSharp;
using Microsoft.VisualBasic;

using WebServer.HTDOCS;

namespace WebServer.Modules
{
	public enum CoreModulePriority
	{
		Disabled = 0,
		Low = 1,
		Medium = 2,
		High = 3,
		Highest = 4
	}

	[AttributeUsage(AttributeTargets.Class)]
	public class CoreModuleAttribute : Attribute
	{
		private string _Name;
		public string Name { get { return _Name; } }

		private Type _TypeOf;
		public Type TypeOf { get { return _TypeOf; } }

		private CoreModulePriority _Priority;
		public CoreModulePriority Priority { get { return _Priority; } }

		public CoreModuleAttribute(string name, Type typeOf, CoreModulePriority priority)
		{
			_Name = name;
			_TypeOf = typeOf;
			_Priority = priority;
		}
	}

	public static class CoreModules
	{
		public delegate void CoreModuleStart();

		private static string _InputDirectory = IOUtility.GetSafeDirectoryPath(Core.BaseDirectory + "\\" + Config.SystemName + "\\Custom\\Modules\\");
		public static void Initialize()
		{
			try
			{
				Type[] ats = new Type[0];
				Type[] cts = new Type[0];
				List<Type> lts = new List<Type>();

				ats = Assembly.GetAssembly(typeof(CoreModuleAttribute)).GetTypes();

				if (Core.CustomAssembly != null)
					cts = Core.CustomAssembly.GetTypes();

				for (int i = 0; i < ats.Length; i++)
					lts.Add(ats[i]);

				for (int i = 0; i < cts.Length; i++)
					lts.Add(cts[i]);

				Type[] ts = lts.ToArray();

				List<CoreModuleAttribute> attrs = new List<CoreModuleAttribute>();
				List<CoreModuleAttribute> fastInvoke = new List<CoreModuleAttribute>();

				int disabled = 0, count = 0;

				foreach (Type t in ts)
				{
					CoreModuleAttribute[] cmas = (CoreModuleAttribute[])t.GetCustomAttributes(typeof(CoreModuleAttribute), true);

					count += cmas.Length;

					foreach (CoreModuleAttribute cma in cmas)
					{
						if (cma.Priority == CoreModulePriority.Disabled)
						{
							disabled++;
							continue;
						}

						if (cma.Priority == CoreModulePriority.Highest)
						{
							fastInvoke.Add(cma);
							continue;
						}

						attrs.Add(cma);
					}
				}

				Core.ConsoleWriteLine("Detected {0} CoreModule{1}; {2} are disabled.",
					count > 0 ? count.ToString("#,#") : "0", count > 1 ? "s" : "", disabled > 0 ? disabled.ToString("#,#") : "0");

				Prioritize(ref fastInvoke);

				for (int i = 0; i < fastInvoke.Count; i++)
				{ InvokeStart(fastInvoke[i]); }

				fastInvoke.Clear();

				Prioritize(ref attrs);

				for (int i = 0; i < attrs.Count; i++)
				{ InvokeStart(attrs[i]); }

				attrs.Clear();
			}
			catch (Exception ex)
			{
				Core.ConsoleWriteLine("*** WARNING: FATAL EXCEPTION THROWN ***");
				Core.ConsoleWriteLine("*** {0} ***", ex);
				Core.ConsoleWriteLine("***************************************");
				Core.WaitForKeyPress("Press any key to continue.");
			}
		}

		public static void InvokeStart(List<CoreModuleAttribute> attrs)
		{
			for (int i = 0; i < attrs.Count; i++)
			{ InvokeStart(attrs[i]); }
		}

		public static void InvokeStart(CoreModuleAttribute cma)
		{
			if (!Core.CheckState(CoreState.Started))
				return;

			CoreModuleStart start = GetStartMethod(cma);

			if (start != null)
			{
				Core.ConsoleWriteLine("CoreModule started: {0}", cma.Name);
				start.Invoke();
			}
			else
			{
				Core.ConsoleWriteError("CoreModule failed: {0} - 'private static void Start()' method could not be found.", cma.Name);
			}
		}

		public static CoreModuleStart GetStartMethod(CoreModuleAttribute cma)
		{
			MethodInfo method = cma.TypeOf.GetMethod("Start", BindingFlags.NonPublic | BindingFlags.Static);

			if (method == null)
				return null;

			return (CoreModuleStart)Delegate.CreateDelegate(typeof(CoreModuleStart), method);
		}

		private static void Prioritize(ref List<CoreModuleAttribute> attrs)
		{
			attrs.Sort(new InternalComparer<CoreModuleAttribute>());
		}

		internal class InternalComparer<T> : IComparer<T> where T : CoreModuleAttribute
		{
			public InternalComparer()
			{ }

			public int Compare(T a, T b)
			{
				if (a.Priority > b.Priority)
					return -1;

				if (a.Priority == b.Priority)
					return 0;

				if (a.Priority < b.Priority)
					return 1;

				return 0;
			}
		}
	}
}
